<?php

namespace Gnway\Ticket\Rest\Task;

use ReflectionException;
use Gnway\Ticket\Rest\Util\FunctionUtils;
use SplPriorityQueue, Iterator, ArrayIterator, Closure;

/**
 * Class TaskExecutor
 *
 * A queue task, Support priority
 *
 * @package Gnway\Ticket\Rest\Task
 */
final class TaskExecutor
{

  /**
   * Instance of Queue
   *
   * @var SplPriorityQueue|null
   */
  private static ?SplPriorityQueue $queue = NULL;

  /**
   * Keep same level queue order
   *
   * @var int
   */
  private static int $serial = PHP_INT_MAX;

  /**
   * Don't let anyone instantiate this class
   */
  private function __construct() {}

  /**
   * Get Queue instance
   *
   * @return SplPriorityQueue
   */
  private static function getInstance(): SplPriorityQueue
  {
    self::$queue || self::$queue = new SplPriorityQueue();

    return self::$queue;
  }

  /**
   * Put task to list
   *
   * @param Closure $task
   * @param int     $priorityLevel
   */
  public static function put(Closure $task, int $priorityLevel = 0): void
  {
    self::getInstance()->insert($task, [$priorityLevel, self::$serial--]);
  }

  /**
   * Clear task list
   *
   * @return void
   */
  public static function clear(): void
  {
    self::$queue = NULL;
    self::$serial = PHP_INT_MAX;
  }

  /**
   * Get iterator
   *
   * @param int $flags
   * @return Iterator
   */
  public static function iterator(int $flags = SplPriorityQueue::EXTR_DATA | SplPriorityQueue::EXTR_PRIORITY): Iterator
  {
    $heap = [];
    $debugInfo = self::getInstance()->__debugInfo();
    end($debugInfo) && $heap = current($debugInfo);
    is_array($heap) || $heap = [];

    switch ($flags) {
      case SplPriorityQueue::EXTR_DATA:
        $heap = array_column($heap, 'data');
        break;
      case SplPriorityQueue::EXTR_PRIORITY:
        $heap = array_column($heap, 'priority');
        break;
    }

    return new ArrayIterator($heap);
  }

  /**
   * Execute task list
   *
   * @return void
   * @throws ReflectionException
   */
  public static function execute(): void
  {
    if (self::$queue === NULL) {
      return;
    }

    foreach (self::getInstance() as $value) {
      FunctionUtils::invoke($value);
    }
  }

}